//
//  Copyright 2016 Apple. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <Security/Security.h>
#import <Security/SecItemPriv.h>
#import <Security/SecKeyPriv.h>
#import <Security/SecAccessControlPriv.h>
#import <libaks_acl_cf_keys.h>
#import "MobileGestalt.h"
#import <CryptoTokenKit/CryptoTokenKit_Private.h>
#import <utilities/SecCFWrappers.h>
#include "featureflags/featureflags.h"

#import "shared_regressions.h"

static id generateKey(id keyType, CFStringRef protection, BOOL withACL, BOOL systemSession) {
    id accessControl;
    if (!withACL) {
        accessControl = CFBridgingRelease(SecAccessControlCreate(kCFAllocatorDefault, NULL));
        SecAccessControlSetProtection((__bridge SecAccessControlRef)accessControl, protection, NULL);
    } else {
        accessControl = CFBridgingRelease(SecAccessControlCreateWithFlags(NULL, protection, kSecAccessControlPrivateKeyUsage, NULL));
    }
    NSDictionary *keyAttributes = @{ (id)kSecAttrTokenID : (id)kSecAttrTokenIDAppleKeyStore,
                                     (id)kSecAttrKeyType : keyType,
                                     (id)kSecAttrAccessControl : accessControl,
                                     (id)kSecAttrIsPermanent : @NO,
#if TARGET_OS_OSX || TARGET_OS_IOS
                                     (id)kSecUseSystemKeychainAlways : @(systemSession),
#endif
    };
    NSError *error;
    id key = (__bridge_transfer id)SecKeyCreateRandomKey((CFDictionaryRef)keyAttributes, (void *)&error);
    ok(key, "failed to create random key %@", error);
    return key;
}

static void secKeySepTest(BOOL testPKA) {
    NSArray *keyTypes;
    if (testPKA) {
        keyTypes = @[(id)kSecAttrKeyTypeECSECPrimeRandom, (id)kSecAttrKeyTypeECSECPrimeRandomPKA, (id)kSecAttrKeyTypeSecureEnclaveAttestation, (id)kSecAttrKeyTypeSecureEnclaveAnonymousAttestation];
    } else {
        keyTypes = @[(id)kSecAttrKeyTypeECSECPrimeRandom, (id)kSecAttrKeyTypeSecureEnclaveAttestation, (id)kSecAttrKeyTypeSecureEnclaveAnonymousAttestation];
    }
    BOOL withACL = NO;
    for (id keyType in keyTypes) {
        id privateKey = generateKey((id)keyType, kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly, (withACL = !withACL), NO);
        ok(privateKey, "failed to create key '%@'", keyType);
        id publicKey = (__bridge_transfer id)SecKeyCopyPublicKey((SecKeyRef)privateKey);

        NSArray *attestaionKeyTypes = @[@(kSecKeySystemKeyTypeSIK), @(kSecKeySystemKeyTypeGID)];
        for (NSNumber *attestationKeyType in attestaionKeyTypes) {
            id attestationKey = (__bridge_transfer id)SecKeyCopySystemKey([attestationKeyType unsignedIntValue], NULL);
            ok(attestationKey, "failed to create attestaion key '%@'", attestationKeyType);
            NSError *error;
            if (![keyType isEqual:(id)kSecAttrKeyTypeSecureEnclaveAttestation] && ![keyType isEqual:(id)kSecAttrKeyTypeSecureEnclaveAnonymousAttestation]) {
                const char rawData[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
                NSData *dataToSign = [NSData dataWithBytes:rawData length:sizeof(rawData)];
                NSData *signedData = (__bridge_transfer NSData*)SecKeyCreateSignature((SecKeyRef)privateKey, kSecKeyAlgorithmECDSASignatureMessageX962SHA256, (__bridge CFDataRef)dataToSign, (void *)&error);
                ok(signedData, "failed to sign data, error %@", error);
                error = nil;
                ok(SecKeyVerifySignature((SecKeyRef)publicKey, kSecKeyAlgorithmECDSASignatureMessageX962SHA256, (__bridge CFDataRef)dataToSign, (__bridge CFDataRef)signedData, (void *)&error),
                   "failed to verify data '%@'", error);

                // Try signing large data.
                dataToSign = [NSMutableData dataWithLength:10 * 1024 * 1024];
                error = nil;
                signedData = (__bridge_transfer NSData*)SecKeyCreateSignature((SecKeyRef)privateKey, kSecKeyAlgorithmECDSASignatureMessageX962SHA256, (__bridge CFDataRef)dataToSign, (void *)&error);
                ok(signedData, "failed to sign data, error %@", error);
                error = nil;
                ok(SecKeyVerifySignature((SecKeyRef)publicKey, kSecKeyAlgorithmECDSASignatureMessageX962SHA256, (__bridge CFDataRef)dataToSign, (__bridge CFDataRef)signedData, (void *)&error),
                   "failed to verify data '%@'", error);
            }
            NSData *attestationData = (__bridge_transfer NSData *)SecKeyCreateAttestation((__bridge SecKeyRef)attestationKey, (__bridge SecKeyRef)privateKey, (void *)&error);
            ok(attestationData, "failed to attest key '%@'", error);
        }

        NSDictionary *keyAttrs = (__bridge_transfer NSDictionary *)SecKeyCopyAttributes((SecKeyRef)privateKey);
        NSData *keyBlob = keyAttrs[(id)kSecAttrTokenOID];

        NSDictionary *params = @{ (id)kSecAttrTokenID : (id)kSecAttrTokenIDAppleKeyStore,
                                  (id)kSecAttrTokenOID : keyBlob,
                                  (id)kSecReturnRef : @YES };

        privateKey = nil;
        NSError *error;
        privateKey = (__bridge_transfer id)SecKeyCreateWithData((__bridge CFDataRef)keyBlob, (__bridge CFDictionaryRef)params, (void *)&error);
        ok(privateKey, "failed to create key with data '%@'", error);
//        <rdar://problem/30651629> SecItemAdd fails to create SecKey from aks key blob
//
//        ok_status(SecItemAdd((__bridge CFDictionaryRef)params, (void *)&privateKey), "failed to create key from aks blob");
//        ok_status(SecItemDelete((__bridge CFDictionaryRef) @{(id)kSecValueRef : privateKey}), "failed to delete key from aks blob");
    }
}

static void attestationTest(CFStringRef protection, BOOL withACL) {
    NSError *error;
    id privKey = generateKey((id)kSecAttrKeyTypeECSECPrimeRandom, protection, withACL, NO);
    id uik = generateKey((id)kSecAttrKeyTypeSecureEnclaveAttestation, protection, withACL, NO);
    id sik = CFBridgingRelease(SecKeyCopySystemKey(kSecKeySystemKeyTypeSIK, (void *)&error));
    ok(sik != nil, "get SIK key: %@", error);
    id dcik = CFBridgingRelease(SecKeyCopySystemKey(kSecKeySystemKeyTypeDCIK, (void *)&error));

    error = nil;
    NSData *attSIKPlain = CFBridgingRelease(SecKeyCreateAttestation((__bridge SecKeyRef)sik, (__bridge SecKeyRef)uik, (void *)&error));
    ok(attSIKPlain != nil, "SIK attesting UIK, no nonce: %@", error);

    error = nil;
    NSData *attUIKPlain = CFBridgingRelease(SecKeyCreateAttestation((__bridge SecKeyRef)uik, (__bridge SecKeyRef)privKey, (void *)&error));
    ok(attUIKPlain != nil, "UIK attesting privKey, no nonce: %@", error);

    if (dcik != nil) {
        error = nil;
        NSData *attDCIKPlain = CFBridgingRelease(SecKeyCreateAttestation((__bridge SecKeyRef)dcik, (__bridge SecKeyRef)privKey, (void *)&error));
        ok(attDCIKPlain != nil, "DCIK attesting privKey, no nonce: %@", error);

        error = nil;
        NSData *attSIKDCIK = CFBridgingRelease(SecKeyCreateAttestation((__bridge SecKeyRef)sik, (__bridge SecKeyRef)dcik, (void *)&error));
        ok(attSIKDCIK != nil, "SIK attesting DCIK, no nonce: %@", error);
    }

    error = nil;
    NSData *nonce = [@"TESTnonce" dataUsingEncoding:NSUTF8StringEncoding];
    ok(SecKeySetParameter((__bridge SecKeyRef)sik, kSecKeyParameterSETokenAttestationNonce, (__bridge CFPropertyListRef)nonce, (void *)&error), "Set nonce to SIK: %@", error);
    NSData *attSIKNonce = CFBridgingRelease(SecKeyCreateAttestation((__bridge SecKeyRef)sik, (__bridge SecKeyRef)uik, (void *)&error));
    ok(attSIKNonce != nil, "SIK attesting UIK, with nonce: %@", error);

    error = nil;
    ok(SecKeySetParameter((__bridge SecKeyRef)uik, kSecKeyParameterSETokenAttestationNonce, (__bridge CFPropertyListRef)nonce, (void *)&error), "Set nonce to UIK: %@", error);
    NSData *attUIKNonce = CFBridgingRelease(SecKeyCreateAttestation((__bridge SecKeyRef)uik, (__bridge SecKeyRef)privKey, (void *)&error));
    ok(attUIKNonce != nil, "SIK attesting UIK, with nonce: %@", error);

    id uikc = CFBridgingRelease(SecKeyCopySystemKey(kSecKeySystemKeyTypeUIKCommitted, (void *)&error));
    ok(uikc != nil, "get UIKC key: %@", error);
    id sysPrivKey = generateKey((id)kSecAttrKeyTypeECSECPrimeRandom, protection, withACL, YES);
    NSData *attSysUIKC = CFBridgingRelease(SecKeyCreateAttestation((__bridge SecKeyRef)uikc, (__bridge SecKeyRef)sysPrivKey, (void *)&error));
    ok(attSysUIKC != nil, "UIKC attesting sysPriv: %@", error);
}

static void sysKeyAttestationTest(CFStringRef protection, BOOL withACL, const char *name, SecKeySystemKeyType committed, SecKeySystemKeyType proposed, BOOL canAttest) {
    NSError *error;
    id privKey = generateKey((id)kSecAttrKeyTypeECSECPrimeRandom, protection, withACL, NO);
    id sik = CFBridgingRelease(SecKeyCopySystemKey(kSecKeySystemKeyTypeSIK, (void *)&error));
    ok(sik != nil, "get SIK key: %@", error);

    id pubSIK = CFBridgingRelease(SecKeyCopyPublicKey((__bridge SecKeyRef)sik));
    ok(pubSIK != nil, "get SIK pubkey");

    id sysKeyC = CFBridgingRelease(SecKeyCopySystemKey(committed, (void *)&error));
    if (sysKeyC == nil) {
        diag("skipping attestation test, platform does not support key %s-committed", name);
        return;
    }

    error = nil;
    id sysKeyP = CFBridgingRelease(SecKeyCopySystemKey(proposed, (void *)&error));
    ok(sysKeyP != nil, "unable to get proposed key, but successfully got committed key");

    if (canAttest) {
        error = nil;
        NSData *attSysKeyC = CFBridgingRelease(SecKeyCreateAttestation((__bridge SecKeyRef)sysKeyC, (__bridge SecKeyRef)privKey, (void *)&error));
        ok(attSysKeyC != nil, "%s-committed attesting privKey: %@", name, error);

        error = nil;
        NSData *attSysKeyP = CFBridgingRelease(SecKeyCreateAttestation((__bridge SecKeyRef)sysKeyP, (__bridge SecKeyRef)privKey, (void *)&error));
        ok(attSysKeyP != nil, "%s-proposed attesting privKey: %@", name, error);
    }

    id pubSysKeyP = CFBridgingRelease(SecKeyCopyPublicKey((__bridge SecKeyRef)sysKeyP));
    ok(pubSysKeyP != nil, "%s-proposed copy public key", name);
    id pubSysKeyC = CFBridgingRelease(SecKeyCopyPublicKey((__bridge SecKeyRef)sysKeyC));
    ok(pubSysKeyC != nil, "%s-committed copy public key", name);

    BOOL res = SecKeyControlLifetime((__bridge SecKeyRef)sysKeyC, kSecKeyControlLifetimeTypeBump, (void *)&error);
    ok(res, "bumping %s: %@", name, error);
    sysKeyP = CFBridgingRelease(SecKeyCopySystemKey(proposed, (void *)&error));
    ok(sysKeyP != nil, "unable to get %s-proposed after bump: %@", name, error);
    id pubSysKeyPN = CFBridgingRelease(SecKeyCopyPublicKey((__bridge SecKeyRef)sysKeyP));
    ok(pubSysKeyPN != nil, "%s-proposed copy public key", name);

    if (canAttest) {
        error = nil;
        NSData *attSysKeyCN = CFBridgingRelease(SecKeyCreateAttestation((__bridge SecKeyRef)sysKeyC, (__bridge SecKeyRef)privKey, (void *)&error));
        ok(attSysKeyCN != nil, "%s-committed attesting privKey: %@", name, error);

        error = nil;
        NSData *attSysKeyPN = CFBridgingRelease(SecKeyCreateAttestation((__bridge SecKeyRef)sysKeyP, (__bridge SecKeyRef)privKey, (void *)&error));
        ok(attSysKeyPN != nil, "%s-proposed attesting privKey: %@", name, error);
    }

    ok(![pubSysKeyPN isEqual:pubSysKeyC], "%s proposed and committed differ after bump (p:%@, c:%@)", name, pubSysKeyPN, pubSysKeyC);

    res = SecKeyControlLifetime((__bridge SecKeyRef)sysKeyP, kSecKeyControlLifetimeTypeCommit, (void *)&error);
    ok(res, "committing %s: %@", name, error);
    sysKeyC = CFBridgingRelease(SecKeyCopySystemKey(committed, (void *)&error));
    ok(sysKeyC != nil, "unable to get %s-committed after commit: %@", name, error);
    id pubSysKeyCN = CFBridgingRelease(SecKeyCopyPublicKey((__bridge SecKeyRef)sysKeyC));
    ok(pubSysKeyCN != nil, "%s-committed copy public key", name);

    if (canAttest) {
        error = nil;
        NSData *attSysKeyCNN = CFBridgingRelease(SecKeyCreateAttestation((__bridge SecKeyRef)sysKeyC, (__bridge SecKeyRef)privKey, (void *)&error));
        ok(attSysKeyCNN != nil, "%s-committed attesting privKey: %@", name, error);

        error = nil;
        NSData *attSysKeyPNN = CFBridgingRelease(SecKeyCreateAttestation((__bridge SecKeyRef)sysKeyP, (__bridge SecKeyRef)privKey, (void *)&error));
        ok(attSysKeyPNN != nil, "%s-proposed attesting privKey: %@", name, error);
    }

    ok([pubSysKeyPN isEqual:pubSysKeyCN], "%s proposed and committed same after commit (p:%@, c:%@)", name, pubSysKeyPN, pubSysKeyCN);

    // Attest system key with SIK
    NSData *attSIKSysKeyP = CFBridgingRelease(SecKeyCreateAttestation((__bridge SecKeyRef)sik, (__bridge SecKeyRef)sysKeyP, (void *)&error));
    ok(attSIKSysKeyP != nil, "SIK attesting %s-proposed, error: %@", name, error);

    NSData *attSIKSysKeyC = CFBridgingRelease(SecKeyCreateAttestation((__bridge SecKeyRef)sik, (__bridge SecKeyRef)sysKeyC, (void *)&error));
    ok(attSIKSysKeyC != nil, "SIK attesting %s-committed, error: %@", name, error);
}

static void keyFromBlobTest(void) {
    NSError *error;
    
    NSDictionary *keyParams = @{ (id)kSecAttrTokenID: (id)kSecAttrTokenIDAppleKeyStore,
                                 (id)kSecAttrKeyType: (id)kSecAttrKeyTypeECSECPrimeRandom,
                                 (id)kSecAttrIsPermanent: @NO
                                 };
    id privateKeyRef = CFBridgingRelease(SecKeyCreateRandomKey((CFDictionaryRef)keyParams, (void *)&error));
    ok(privateKeyRef != nil, "Failed to create a random key: %@", error);

    NSDictionary *keyAttrs = CFBridgingRelease(SecKeyCopyAttributes((SecKeyRef)privateKeyRef));
    NSData *keyBlob = keyAttrs[(id)kSecAttrTokenOID];
    
    // keyBlob -> SecKey:
    NSDictionary *params = @{ (id)kSecAttrTokenID : (id)kSecAttrTokenIDAppleKeyStore,
                              (id)kSecAttrTokenOID : keyBlob };
    id newKeyRef = CFBridgingRelease(SecKeyCreateWithData((CFDataRef)keyBlob, (CFDictionaryRef)params, (void *)&error));
    ok(newKeyRef != nil, "Failed to create key from data: %@", error);

    id ref;
    is_status(SecItemCopyMatching(((__bridge CFDictionaryRef) @{(id)kSecClass: (id)kSecClassKey, (id)kSecValueRef: newKeyRef}), (void *)&ref), errSecItemNotFound);
}

static const uint8_t satori_priv[] = {
    0x04, 0xe4, 0xef, 0x00, 0x27, 0xcb, 0xa6, 0x46,
    0x0d, 0xa6, 0xbd, 0x77, 0x14, 0x65, 0xe5, 0x5a, 0x14, 0xc9, 0xf8, 0xd8, 0xdd, 0x4c, 0x70, 0x44,
    0x50, 0x49, 0xe4, 0xfa, 0x24, 0x71, 0xaa, 0x4c, 0xe2, 0x74, 0x3b, 0xfd, 0x23, 0xda, 0x6f, 0x92,
    0x04, 0x4c, 0x93, 0x6c, 0xea, 0x8a, 0xac, 0x22, 0x99, 0xd9, 0x6e, 0x3f, 0xed, 0x20, 0xfd, 0xdd,
    0x95, 0xe2, 0x32, 0xa0, 0xeb, 0x23, 0xa2, 0xd2, 0x8b, 0x23, 0xcf, 0x74, 0xb4, 0x76, 0x93, 0xdf,
    0x6d, 0x31, 0x63, 0xc9, 0x87, 0x85, 0x3f, 0x44, 0x09, 0x1f, 0x0d, 0xe2, 0x9a, 0x94, 0x29, 0x03,
    0x70, 0xbf, 0x87, 0x2a, 0x7e, 0xac, 0xa8, 0x8d, 0x11,
};

static const uint8_t satori_test_cert[] = {
    0x30, 0x82, 0x03, 0xf2, 0x30, 0x82, 0x03, 0x98, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x08, 0x75,
    0x0e, 0x97, 0x07, 0xb3, 0x6e, 0x48, 0xe9, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d,
    0x04, 0x03, 0x02, 0x30, 0x7f, 0x31, 0x33, 0x30, 0x31, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x2a,
    0x54, 0x65, 0x73, 0x74, 0x20, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x41, 0x70, 0x70, 0x6c, 0x69,
    0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69,
    0x6f, 0x6e, 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, 0x47, 0x33, 0x31, 0x26, 0x30, 0x24, 0x06, 0x03,
    0x55, 0x04, 0x0b, 0x0c, 0x1d, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69,
    0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
    0x74, 0x79, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x0a, 0x41, 0x70, 0x70,
    0x6c, 0x65, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
    0x13, 0x02, 0x55, 0x53, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x38, 0x30, 0x39, 0x32, 0x37, 0x31, 0x38,
    0x33, 0x31, 0x30, 0x37, 0x5a, 0x17, 0x0d, 0x32, 0x30, 0x31, 0x30, 0x32, 0x36, 0x31, 0x38, 0x33,
    0x31, 0x30, 0x37, 0x5a, 0x30, 0x73, 0x31, 0x17, 0x30, 0x15, 0x06, 0x0a, 0x09, 0x92, 0x26, 0x89,
    0x93, 0xf2, 0x2c, 0x64, 0x01, 0x01, 0x0c, 0x07, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x31,
    0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x0c, 0x54, 0x65, 0x73, 0x74, 0x20, 0x53,
    0x50, 0x20, 0x4c, 0x65, 0x61, 0x66, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c,
    0x19, 0x31, 0x2e, 0x32, 0x2e, 0x38, 0x34, 0x30, 0x2e, 0x31, 0x31, 0x33, 0x36, 0x33, 0x35, 0x2e,
    0x31, 0x30, 0x30, 0x2e, 0x36, 0x2e, 0x36, 0x34, 0x2e, 0x33, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03,
    0x55, 0x04, 0x0a, 0x0c, 0x07, 0x54, 0x65, 0x73, 0x74, 0x20, 0x53, 0x50, 0x31, 0x0b, 0x30, 0x09,
    0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a,
    0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07,
    0x03, 0x42, 0x00, 0x04, 0xe4, 0xef, 0x00, 0x27, 0xcb, 0xa6, 0x46, 0x0d, 0xa6, 0xbd, 0x77, 0x14,
    0x65, 0xe5, 0x5a, 0x14, 0xc9, 0xf8, 0xd8, 0xdd, 0x4c, 0x70, 0x44, 0x50, 0x49, 0xe4, 0xfa, 0x24,
    0x71, 0xaa, 0x4c, 0xe2, 0x74, 0x3b, 0xfd, 0x23, 0xda, 0x6f, 0x92, 0x04, 0x4c, 0x93, 0x6c, 0xea,
    0x8a, 0xac, 0x22, 0x99, 0xd9, 0x6e, 0x3f, 0xed, 0x20, 0xfd, 0xdd, 0x95, 0xe2, 0x32, 0xa0, 0xeb,
    0x23, 0xa2, 0xd2, 0x8b, 0xa3, 0x82, 0x02, 0x08, 0x30, 0x82, 0x02, 0x04, 0x30, 0x0c, 0x06, 0x03,
    0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d,
    0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xb9, 0x04, 0x1a, 0x95, 0x5b, 0x6b, 0x91, 0x04, 0x39,
    0xea, 0x70, 0x2a, 0x47, 0xb7, 0xa8, 0x49, 0x36, 0xe4, 0x4d, 0xdb, 0x30, 0x4d, 0x06, 0x08, 0x2b,
    0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x41, 0x30, 0x3f, 0x30, 0x3d, 0x06, 0x08, 0x2b,
    0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x31, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
    0x6f, 0x63, 0x73, 0x70, 0x2d, 0x75, 0x61, 0x74, 0x2e, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x61, 0x70,
    0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x30, 0x33, 0x2d, 0x74,
    0x73, 0x61, 0x61, 0x69, 0x63, 0x61, 0x67, 0x33, 0x31, 0x30, 0x30, 0x82, 0x01, 0x03, 0x06, 0x03,
    0x55, 0x1d, 0x20, 0x04, 0x81, 0xfb, 0x30, 0x81, 0xf8, 0x30, 0x81, 0xf5, 0x06, 0x09, 0x2a, 0x86,
    0x48, 0x86, 0xf7, 0x63, 0x64, 0x05, 0x01, 0x30, 0x81, 0xe7, 0x30, 0x81, 0xac, 0x06, 0x08, 0x2b,
    0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x02, 0x30, 0x81, 0x9f, 0x0c, 0x81, 0x9c, 0x52, 0x65, 0x6c,
    0x69, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x63, 0x65,
    0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x62, 0x79, 0x20, 0x61, 0x6e, 0x79,
    0x20, 0x70, 0x61, 0x72, 0x74, 0x79, 0x20, 0x69, 0x73, 0x20, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63,
    0x74, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69,
    0x63, 0x61, 0x74, 0x65, 0x20, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2c, 0x20, 0x43, 0x65, 0x72,
    0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x50, 0x72, 0x61, 0x63, 0x74,
    0x69, 0x63, 0x65, 0x20, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2c, 0x20, 0x61,
    0x6e, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, 0x65, 0x72, 0x6d, 0x73, 0x20, 0x6f, 0x66, 0x20,
    0x61, 0x6e, 0x79, 0x20, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x61,
    0x67, 0x72, 0x65, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x30, 0x36, 0x06, 0x08, 0x2b, 0x06, 0x01,
    0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x2a, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77,
    0x77, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x65, 0x72, 0x74,
    0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79,
    0x2f, 0x30, 0x3c, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x35, 0x30, 0x33, 0x30, 0x31, 0xa0, 0x2f,
    0xa0, 0x2d, 0x86, 0x2b, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2d, 0x75,
    0x61, 0x74, 0x2e, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f,
    0x6d, 0x2f, 0x74, 0x73, 0x61, 0x61, 0x69, 0x63, 0x61, 0x67, 0x33, 0x2e, 0x63, 0x72, 0x6c, 0x30,
    0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xa0, 0xe6, 0xdf, 0x03, 0xb1, 0x2a,
    0x53, 0x40, 0x35, 0xc4, 0x01, 0x4b, 0x6a, 0xbd, 0x35, 0x8f, 0x6d, 0x28, 0x63, 0xba, 0x30, 0x0e,
    0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x03, 0x28, 0x30, 0x10,
    0x06, 0x0a, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x63, 0x64, 0x06, 0x40, 0x03, 0x04, 0x02, 0x05, 0x00,
    0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03, 0x48, 0x00, 0x30,
    0x45, 0x02, 0x20, 0x6a, 0x7e, 0xf1, 0x0b, 0x60, 0xba, 0x4c, 0x3c, 0x83, 0xd5, 0xbd, 0x4a, 0xb1,
    0x62, 0x2f, 0x52, 0x92, 0xba, 0xb9, 0x64, 0xcd, 0xaa, 0x63, 0x96, 0xa6, 0xd8, 0x6d, 0x3a, 0xf3,
    0x83, 0x81, 0xb9, 0x02, 0x21, 0x00, 0xc2, 0x37, 0x2d, 0x3a, 0xb7, 0x03, 0x81, 0x2f, 0x3e, 0xf1,
    0x32, 0x98, 0x43, 0x27, 0xbb, 0x64, 0xbf, 0xfb, 0xb9, 0x9a, 0x0c, 0xad, 0x9a, 0x98, 0x6f, 0xbc,
    0x87, 0x30, 0xfe, 0xfe, 0x3c, 0x2e, 0x30, 0x82, 0x03, 0x17, 0x30, 0x82, 0x02, 0x9e, 0xa0, 0x03,
    0x02, 0x01, 0x02, 0x02, 0x08, 0x5b, 0x7d, 0xce, 0x90, 0x32, 0x77, 0x34, 0xd6, 0x30, 0x0a, 0x06,
    0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x03, 0x30, 0x6c, 0x31, 0x20, 0x30, 0x1e, 0x06,
    0x03, 0x55, 0x04, 0x03, 0x0c, 0x17, 0x54, 0x65, 0x73, 0x74, 0x20, 0x41, 0x70, 0x70, 0x6c, 0x65,
    0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, 0x47, 0x33, 0x31, 0x26, 0x30,
    0x24, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x1d, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x43, 0x65,
    0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68,
    0x6f, 0x72, 0x69, 0x74, 0x79, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x0a,
    0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
    0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x37, 0x30, 0x35, 0x33,
    0x30, 0x32, 0x31, 0x33, 0x35, 0x35, 0x35, 0x5a, 0x17, 0x0d, 0x33, 0x32, 0x30, 0x35, 0x32, 0x36,
    0x32, 0x31, 0x33, 0x35, 0x35, 0x35, 0x5a, 0x30, 0x7f, 0x31, 0x33, 0x30, 0x31, 0x06, 0x03, 0x55,
    0x04, 0x03, 0x0c, 0x2a, 0x54, 0x65, 0x73, 0x74, 0x20, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x41,
    0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x67,
    0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, 0x47, 0x33, 0x31, 0x26,
    0x30, 0x24, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x1d, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x43,
    0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74,
    0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c,
    0x0a, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x0b, 0x30, 0x09, 0x06,
    0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86,
    0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03,
    0x42, 0x00, 0x04, 0xfa, 0xdc, 0xcc, 0x11, 0x10, 0x74, 0x7b, 0x30, 0xc4, 0x69, 0xdd, 0x65, 0xc3,
    0xda, 0xa1, 0x55, 0xdf, 0xeb, 0x09, 0x5c, 0x29, 0xd0, 0x50, 0x3e, 0x1c, 0x0a, 0x34, 0xfa, 0x83,
    0xb1, 0x79, 0x49, 0x4d, 0x9d, 0xb3, 0xb9, 0x46, 0xa1, 0xc9, 0x43, 0x67, 0xb3, 0x03, 0x45, 0xd3,
    0xa4, 0x01, 0x60, 0xc3, 0x58, 0xdb, 0x98, 0x83, 0x19, 0x32, 0xce, 0xc5, 0xa3, 0x68, 0x38, 0xb6,
    0xca, 0x4d, 0x63, 0xa3, 0x82, 0x01, 0x15, 0x30, 0x82, 0x01, 0x11, 0x30, 0x53, 0x06, 0x08, 0x2b,
    0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x47, 0x30, 0x45, 0x30, 0x43, 0x06, 0x08, 0x2b,
    0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x37, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
    0x6f, 0x63, 0x73, 0x70, 0x2d, 0x75, 0x61, 0x74, 0x2e, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x61, 0x70,
    0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x30, 0x34, 0x2d, 0x74,
    0x65, 0x73, 0x74, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x72, 0x6f, 0x6f, 0x74, 0x63, 0x61, 0x67, 0x33,
    0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xb9, 0x04, 0x1a, 0x95, 0x5b,
    0x6b, 0x91, 0x04, 0x39, 0xea, 0x70, 0x2a, 0x47, 0xb7, 0xa8, 0x49, 0x36, 0xe4, 0x4d, 0xdb, 0x30,
    0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff,
    0x02, 0x01, 0x00, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14,
    0xfc, 0x46, 0xd8, 0x83, 0x6c, 0x1f, 0xe6, 0xf2, 0xdc, 0xdf, 0xa7, 0x99, 0x17, 0xae, 0x0b, 0x44,
    0x67, 0x17, 0x1b, 0x46, 0x30, 0x44, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x3d, 0x30, 0x3b, 0x30,
    0x39, 0xa0, 0x37, 0xa0, 0x35, 0x86, 0x33, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72,
    0x6c, 0x2d, 0x75, 0x61, 0x74, 0x2e, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65,
    0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x72, 0x6f,
    0x6f, 0x74, 0x63, 0x61, 0x67, 0x33, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d,
    0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x10, 0x06, 0x0a, 0x2a, 0x86,
    0x48, 0x86, 0xf7, 0x63, 0x64, 0x06, 0x02, 0x0e, 0x04, 0x02, 0x05, 0x00, 0x30, 0x0a, 0x06, 0x08,
    0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x03, 0x03, 0x67, 0x00, 0x30, 0x64, 0x02, 0x30, 0x6a,
    0x26, 0x0d, 0x2a, 0x80, 0xcd, 0x69, 0x33, 0xef, 0x50, 0xbb, 0x78, 0xbc, 0x17, 0x4c, 0xcd, 0xa6,
    0x6b, 0x86, 0xe2, 0x86, 0xd3, 0xe7, 0x3d, 0xc3, 0x8f, 0x01, 0xd8, 0x83, 0xe6, 0xc8, 0x1c, 0x7d,
    0xe7, 0x78, 0xca, 0xfd, 0x29, 0xd5, 0xfa, 0x32, 0x63, 0x98, 0xdb, 0x65, 0x17, 0x2e, 0x05, 0x02,
    0x30, 0x4d, 0xd7, 0x31, 0x32, 0xfa, 0x17, 0x73, 0x50, 0x9c, 0xb6, 0x04, 0x1d, 0xca, 0xa6, 0x1f,
    0x60, 0x0a, 0x72, 0x59, 0x6d, 0x7f, 0xc9, 0x5b, 0x93, 0x4a, 0x13, 0x40, 0x60, 0xae, 0x6c, 0x13,
    0x43, 0xd2, 0x71, 0xc2, 0xdd, 0x32, 0xaa, 0x90, 0xa9, 0xc5, 0xe2, 0xdd, 0x32, 0x23, 0x2f, 0xaa,
    0xda, 0x30, 0x82, 0x02, 0x4c, 0x30, 0x82, 0x01, 0xd3, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x08,
    0x78, 0x36, 0x0b, 0xf4, 0xb7, 0xc8, 0xb6, 0xb0, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce,
    0x3d, 0x04, 0x03, 0x03, 0x30, 0x6c, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c,
    0x17, 0x54, 0x65, 0x73, 0x74, 0x20, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x52, 0x6f, 0x6f, 0x74,
    0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, 0x47, 0x33, 0x31, 0x26, 0x30, 0x24, 0x06, 0x03, 0x55, 0x04,
    0x0b, 0x0c, 0x1d, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69,
    0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79,
    0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x0a, 0x41, 0x70, 0x70, 0x6c, 0x65,
    0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
    0x55, 0x53, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x35, 0x30, 0x34, 0x32, 0x32, 0x30, 0x33, 0x31, 0x37,
    0x34, 0x34, 0x5a, 0x17, 0x0d, 0x34, 0x30, 0x31, 0x32, 0x32, 0x36, 0x30, 0x33, 0x31, 0x33, 0x33,
    0x37, 0x5a, 0x30, 0x6c, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x17, 0x54,
    0x65, 0x73, 0x74, 0x20, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43,
    0x41, 0x20, 0x2d, 0x20, 0x47, 0x33, 0x31, 0x26, 0x30, 0x24, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c,
    0x1d, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
    0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31, 0x13,
    0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x0a, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x49,
    0x6e, 0x63, 0x2e, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53,
    0x30, 0x76, 0x30, 0x10, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x05, 0x2b,
    0x81, 0x04, 0x00, 0x22, 0x03, 0x62, 0x00, 0x04, 0xa9, 0x1a, 0x63, 0x34, 0xef, 0xbc, 0xa6, 0x8a,
    0xd6, 0x2a, 0x6a, 0x38, 0x22, 0xe9, 0x25, 0xad, 0xda, 0x28, 0xa0, 0x49, 0xc5, 0x64, 0xfe, 0x5d,
    0x91, 0xc3, 0x6c, 0xf7, 0x99, 0xe4, 0xba, 0xe4, 0x2a, 0x5f, 0x61, 0xd2, 0xbf, 0x3b, 0x6c, 0xa8,
    0x61, 0x11, 0xb5, 0xe0, 0x66, 0xf7, 0x22, 0x11, 0x86, 0x97, 0x5d, 0xc3, 0xba, 0x1b, 0x6d, 0x55,
    0x7f, 0xd0, 0xf9, 0x80, 0xe0, 0xff, 0xd9, 0x05, 0xad, 0x5a, 0x5b, 0xbf, 0x3a, 0x7a, 0xa7, 0x09,
    0x52, 0x1a, 0x31, 0x7f, 0x0c, 0xa2, 0xe8, 0x10, 0xf5, 0x36, 0xd3, 0xc8, 0xea, 0xa0, 0x5b, 0x0a,
    0x28, 0x85, 0x30, 0x28, 0x5f, 0x94, 0xf6, 0x94, 0xa3, 0x42, 0x30, 0x40, 0x30, 0x1d, 0x06, 0x03,
    0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xfc, 0x46, 0xd8, 0x83, 0x6c, 0x1f, 0xe6, 0xf2, 0xdc,
    0xdf, 0xa7, 0x99, 0x17, 0xae, 0x0b, 0x44, 0x67, 0x17, 0x1b, 0x46, 0x30, 0x0f, 0x06, 0x03, 0x55,
    0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0e, 0x06, 0x03,
    0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x0a, 0x06, 0x08,
    0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x03, 0x03, 0x67, 0x00, 0x30, 0x64, 0x02, 0x30, 0x1a,
    0x14, 0x38, 0x24, 0xff, 0xb4, 0x08, 0xcb, 0xea, 0xc9, 0x3b, 0xda, 0xcc, 0x82, 0xf3, 0xd9, 0x0d,
    0xd1, 0x2b, 0x6e, 0xbf, 0x1f, 0xc4, 0x15, 0x14, 0x44, 0xdf, 0x98, 0x9b, 0xd7, 0xdd, 0xba, 0x1b,
    0xbe, 0x4f, 0x9f, 0x17, 0xa4, 0xd2, 0x02, 0x75, 0x90, 0x7d, 0x76, 0xcc, 0x93, 0x16, 0x2f, 0x02,
    0x30, 0x02, 0xd7, 0xda, 0x0b, 0xbe, 0xdd, 0x3d, 0xed, 0xf9, 0xa3, 0x06, 0x90, 0xa9, 0x58, 0xbd,
    0x6b, 0x7c, 0x7c, 0xe5, 0xc5, 0x4e, 0x0e, 0x44, 0xa2, 0x94, 0x2f, 0xb4, 0x04, 0x9a, 0xcd, 0x9b,
    0x69, 0x8d, 0x2a, 0xc6, 0x1d, 0x58, 0xff, 0xe3, 0x32, 0xb6, 0xdb, 0x3e, 0x34, 0xff, 0x67, 0x70,
    0xf1
};

static void rewrapTest(void) {
    id accessControl;
    accessControl = CFBridgingRelease(SecAccessControlCreate(kCFAllocatorDefault, NULL));
    SecAccessControlSetProtection((__bridge SecAccessControlRef)accessControl, kSecAttrAccessibleWhenUnlocked, NULL);
    SecAccessControlSetConstraints((__bridge SecAccessControlRef)accessControl, (__bridge CFDictionaryRef)@{(id)kAKSKeyOpECIESTranscode: @YES});

    NSDictionary *keyAttributes = @{ (id)kSecAttrTokenID : (id)kSecAttrTokenIDAppleKeyStore,
                                     (id)kSecAttrKeyType : (id)kSecAttrKeyTypeECSECPrimeRandom,
                                     (id)kSecKeyApplePayEnabled: @YES,
                                     (id)kSecAttrAccessControl : accessControl,
                                     (id)kSecAttrIsPermanent : @NO };
    NSError *error;
    id key = (__bridge_transfer id)SecKeyCreateRandomKey((CFDictionaryRef)keyAttributes, (void *)&error);
    ok(key, "failed to create random key %@", error);

    // Encrypt message with SEP key.
    NSData *message = [@"message" dataUsingEncoding:NSUTF8StringEncoding];
    id pubKey = CFBridgingRelease(SecKeyCopyPublicKey((SecKeyRef)key));
    NSData *encrypted = CFBridgingRelease(SecKeyCreateEncryptedDataWithParameters((__bridge SecKeyRef)pubKey, kSecKeyAlgorithmECIESEncryptionStandardVariableIVX963SHA256AESGCM, (__bridge CFDataRef)message, (__bridge CFDictionaryRef)@{(id)kSecKeyEncryptionParameterSymmetricKeySizeInBits: @128}, (void *)&error));
    ok(encrypted, "failed to encrypt with public key, %@", error);
    NSData *cert = [NSData dataWithBytes:satori_test_cert length:sizeof(satori_test_cert)];
    NSDictionary *recryptParams = @{
                                    (id)kSecKeyEncryptionParameterRecryptCertificate: cert,
                                    (id)kSecKeyEncryptionParameterSymmetricKeySizeInBits: @128,
                                    (id)kSecKeyEncryptionParameterRecryptParameters: @{
                                            (id)kSecKeyEncryptionParameterSymmetricKeySizeInBits: @128
                                            },
                                    };
    NSData *recrypted = CFBridgingRelease(SecKeyCreateDecryptedDataWithParameters((__bridge SecKeyRef)key, kSecKeyAlgorithmECIESEncryptionStandardVariableIVX963SHA256AESGCM, (__bridge CFDataRef)encrypted, (__bridge CFDictionaryRef)recryptParams, (void *)&error));
    ok(recrypted, "failed to recrypt, %@", error);

    id recryptKey = CFBridgingRelease(SecKeyCreateWithData((CFDataRef)[NSData dataWithBytes:satori_priv length:sizeof(satori_priv)], (CFDictionaryRef)@{(id)kSecAttrKeyType: (id)kSecAttrKeyTypeECSECPrimeRandom, (id)kSecAttrKeyClass: (id)kSecAttrKeyClassPrivate}, (void *)&error));
    NSData *decrypted = CFBridgingRelease(SecKeyCreateDecryptedData((__bridge SecKeyRef)recryptKey, kSecKeyAlgorithmECIESEncryptionStandardVariableIVX963SHA256AESGCM, (__bridge CFDataRef)recrypted, (void *)&error));
    ok(decrypted, "failed to decrypt, %@", error);
    ok([decrypted isEqualToData:message], "Decrypted data differs: %@ vs %@", decrypted, message);
}

static void keychainTest(void) {
    id accessControl = CFBridgingRelease(SecAccessControlCreateWithFlags(NULL, kSecAttrAccessibleWhenUnlocked, kSecAccessControlPrivateKeyUsage, NULL));
    NSDictionary *keyAttributes = @{ (id)kSecAttrTokenID : (id)kSecAttrTokenIDAppleKeyStore,
                                     (id)kSecAttrKeyType : (id)kSecAttrKeyTypeECSECPrimeRandom,
                                     (id)kSecPrivateKeyAttrs : @{
                                             (id)kSecAttrAccessControl : accessControl,
                                             (id)kSecAttrIsPermanent : @YES,
                                             (id)kSecAttrLabel : @"si_44_seckey_aks_1",
                                     },
    };
    NSError *error;
    id key = (__bridge_transfer id)SecKeyCreateRandomKey((CFDictionaryRef)keyAttributes, (void *)&error);
    ok(key, "failed to create random key %@", error);
    id pubKey = CFBridgingRelease(SecKeyCopyPublicKey((SecKeyRef)key));
    ok(pubKey, "failed to get public key from SEP key");
    key = nil;

    NSDictionary *query = @{
        (id)kSecReturnRef: @YES,
        (id)kSecClass: (id)kSecClassKey,
        (id)kSecAttrLabel: @"si_44_seckey_aks_1",
    };
    OSStatus status = SecItemCopyMatching((CFDictionaryRef)query, (void *)&key);
    is(status, errSecSuccess, "getting SEP key from keychain failed");

    NSError *err;
    NSData *data = [@"message" dataUsingEncoding:NSUTF8StringEncoding];
    NSData *sig = CFBridgingRelease(SecKeyCreateSignature((SecKeyRef)key, kSecKeyAlgorithmECDSASignatureMessageX962SHA256, (CFDataRef)data, (void *)&err));
    ok(sig, "failed to create signature: %@", err);
    ok(SecKeyVerifySignature((SecKeyRef)pubKey, kSecKeyAlgorithmECDSASignatureMessageX962SHA256, (CFDataRef)data, (CFDataRef)sig, (void *)&err), "failed to verify signature: %@", err);

    status = SecItemDelete((CFDictionaryRef)query);
    is(status, errSecSuccess, "deleting SEP key from keychain failed");

    status = SecItemCopyMatching((CFDictionaryRef)query, (void *)&key);
    is(status, errSecItemNotFound, "SEP key was not deleted from keychain");
}

static void secAccessControlDescriptionTest(void) {
    NSError *error;
    NSObject *ac = CFBridgingRelease(SecAccessControlCreate(kCFAllocatorDefault, (void *)&error));
    ok(ac, "failed to create ac: %@", error);
    ok(SecAccessControlSetProtection((__bridge SecAccessControlRef)ac, kSecAttrAccessibleWhenUnlocked, (void *)&error), "failed to set protection: %@", error);

    NSString *desc = ac.description;
    eq_cf((__bridge CFTypeRef)desc, (__bridge CFTypeRef)@"<SecAccessControlRef: ak>", "unexpected desc: %@", desc);

    SecAccessControlSetConstraints((__bridge SecAccessControlRef)ac, (__bridge CFDictionaryRef)@{});
    desc = ac.description;
    eq_cf((__bridge CFTypeRef)desc, (__bridge CFTypeRef)@"<SecAccessControlRef: ak>", "unexpected desc: %@", desc);

    SecAccessControlSetConstraints((__bridge SecAccessControlRef)ac, (__bridge CFDictionaryRef)@{@"od": (__bridge id)kCFBooleanTrue});
    desc = ac.description;
    eq_cf((__bridge CFTypeRef)desc, (__bridge CFTypeRef)@"<SecAccessControlRef: ak;od(true)>", "unexpected desc: %@", desc);

    SecAccessControlSetConstraints((__bridge SecAccessControlRef)ac, (__bridge CFDictionaryRef)@{@"od": (__bridge id)kCFBooleanTrue, @"oe": (__bridge id)kCFBooleanFalse});
    desc = ac.description;
    eq_cf((__bridge CFTypeRef)desc, (__bridge CFTypeRef)@"<SecAccessControlRef: ak;od(true);oe(false)>", "unexpected desc: %@", desc);

    SecAccessControlSetConstraints((__bridge SecAccessControlRef)ac, (__bridge CFDictionaryRef)@{@"od": @"huh"});
    desc = ac.description;
    eq_cf((__bridge CFTypeRef)desc, (__bridge CFTypeRef)@"<SecAccessControlRef: ak;od(huh)>", "unexpected desc: %@", desc);

    SecAccessControlSetConstraints((__bridge SecAccessControlRef)ac, (__bridge CFDictionaryRef)@{@"od": @2});
    desc = ac.description;
    eq_cf((__bridge CFTypeRef)desc, (__bridge CFTypeRef)@"<SecAccessControlRef: ak;od(2)>", "unexpected desc: %@", desc);

    NSData *shortData = [NSData dataWithBytes:"\x01\x02\x03" length:3];
    SecAccessControlSetConstraints((__bridge SecAccessControlRef)ac, (__bridge CFDictionaryRef)@{@"od": shortData});
    desc = ac.description;
    eq_cf((__bridge CFTypeRef)desc, (__bridge CFTypeRef)@"<SecAccessControlRef: ak;od(010203)>", "unexpected desc: %@", desc);

    NSData *longData = [NSMutableData dataWithLength:128];
    SecAccessControlSetConstraints((__bridge SecAccessControlRef)ac, (__bridge CFDictionaryRef)@{@"od": longData});
    desc = ac.description;
    eq_cf((__bridge CFTypeRef)desc, (__bridge CFTypeRef)@"<SecAccessControlRef: ak;od(00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000...(128b))>", "unexpected desc: %@", desc);

    SecAccessControlSetConstraints((__bridge SecAccessControlRef)ac, (__bridge CFDictionaryRef)@{@"od": @{@"kofn": @2}});
    desc = ac.description;
    eq_cf((__bridge CFTypeRef)desc, (__bridge CFTypeRef)@"<SecAccessControlRef: ak;od(kofn(2))>", "unexpected desc: %@", desc);

    SecAccessControlSetConstraints((__bridge SecAccessControlRef)ac, (__bridge CFDictionaryRef)@{@"od": @{@"cag": @[]}});
    desc = ac.description;
    eq_cf((__bridge CFTypeRef)desc, (__bridge CFTypeRef)@"<SecAccessControlRef: ak;od(cag([]))>", "unexpected desc: %@", desc);

    SecAccessControlSetConstraints((__bridge SecAccessControlRef)ac, (__bridge CFDictionaryRef)@{@"od": @{@"cag": @[@"ag1", @{@"k1": @1, @"k2": @2}]}});
    desc = ac.description;
    eq_cf((__bridge CFTypeRef)desc, (__bridge CFTypeRef)@"<SecAccessControlRef: ak;od(cag([ag1,k1(1)k2(2)]))>", "unexpected desc: %@", desc);
}

static void sysKeySignTest(SecKeySystemKeyType keyType, SecKeyAlgorithm algorithm, NSInteger keySizeInBits, const char *name) {
    NSError *error;
    id privKey = CFBridgingRelease(SecKeyCopySystemKey(keyType, (void *)&error));
    if (privKey == nil) {
        // This key is not supported on this platform.
        return;
    }
    ok(privKey, "getting key type %s failed: %@", name, error);

    is((keySizeInBits + 7) / 8, (NSInteger)SecKeyGetBlockSize((SecKeyRef)privKey), "unexpected key size of private key %s", name);

    NSData *message = [@"message" dataUsingEncoding:NSUTF8StringEncoding];
    NSData *signature = CFBridgingRelease(SecKeyCreateSignature((SecKeyRef)privKey, algorithm, (CFDataRef)message, (void *)&error));
    ok(signature, "signing with key %s failed: %@", name, error);

    id pubKey = CFBridgingRelease(SecKeyCopyPublicKey((SecKeyRef)privKey));
    ok(pubKey, "getting public key of %s failed", name);

    is((keySizeInBits + 7) / 8, (NSInteger)SecKeyGetBlockSize((SecKeyRef)pubKey), "unexpected key size of public key %s", name);

    ok(SecKeyVerifySignature((SecKeyRef)pubKey, algorithm, (CFDataRef)message, (CFDataRef)signature, (void *)&error), "signature verification with key %s failed: %@", name, error);
}

static void systemSessionKeysTest(void) {
#if TARGET_OS_OSX || (TARGET_OS_IOS && !TARGET_OS_SIMULATOR)
    NSError *error;
    id privKey = generateKey((__bridge id)kSecAttrKeyTypeECSECPrimeRandom, kSecAttrAccessibleAlwaysPrivate, NO, YES);
    isnt(privKey, nil, "unable to generate key: %@", error);

    NSData *message = [@"message" dataUsingEncoding:NSUTF8StringEncoding];
    SecKeyAlgorithm algorithm = kSecKeyAlgorithmECDSASignatureMessageX962SHA256;
    NSData *signature;

    signature = CFBridgingRelease(SecKeyCreateSignature((SecKeyRef)privKey, algorithm, (CFDataRef)message, (void *)&error));
    isnt(signature, nil, "signing with system session key failed: %@", error);

    id pubKey = CFBridgingRelease(SecKeyCopyPublicKey((SecKeyRef)privKey));
    isnt(pubKey, nil, "getting public key of system session key failed");
    ok(SecKeyVerifySignature((SecKeyRef)pubKey, algorithm, (CFDataRef)message, (CFDataRef)signature, (void *)&error), "signature verification with system session key failed: %@", error);

#if TARGET_OS_OSX
    // Re-create key from OID blob - must fail when using in user session, must work when done in system session.
    NSDictionary *attribs = CFBridgingRelease(SecKeyCopyAttributes((__bridge SecKeyRef)privKey));
    NSDictionary *params;
    id createdKey;

    params = @{ (id)kSecAttrTokenID: attribs[(id)kSecAttrTokenID], (id)kSecAttrTokenOID: attribs[(id)kSecAttrTokenOID] };
    createdKey = CFBridgingRelease(SecKeyCreateWithData((__bridge CFDataRef)NSData.data, (__bridge CFDictionaryRef)params, (void *)&error));
    signature = CFBridgingRelease(SecKeyCreateSignature((SecKeyRef)createdKey, algorithm, (CFDataRef)message, (void *)&error));
    is(signature, nil, "system session key restored in user context unexpectedly succeeded");

    params = @{ (id)kSecAttrTokenID: attribs[(id)kSecAttrTokenID], (id)kSecAttrTokenOID: attribs[(id)kSecAttrTokenOID],
                (id)kSecUseSystemKeychainAlways: @YES,
    };
    error = nil;
    createdKey = CFBridgingRelease(SecKeyCreateWithData((__bridge CFDataRef)NSData.data, (__bridge CFDictionaryRef)params, (void *)&error));
    signature = CFBridgingRelease(SecKeyCreateSignature((SecKeyRef)createdKey, algorithm, (CFDataRef)message, (void *)&error));
    isnt(signature, nil, "system session key restored in system context failed: %@", error);

    id itemCreatedKey = CFBridgingRelease(SecKeyCreateRandomKey((__bridge CFDictionaryRef)@{
        (id)kSecAttrTokenID: (id)kSecAttrTokenIDSecureEnclave,
        (id)kSecAttrKeyType: (id)kSecAttrKeyTypeECSECPrimeRandom,
        (id)kSecPrivateKeyAttrs: @{
            (id)kSecUseSystemKeychainAlways: @YES,
            (id)kSecAttrIsPermanent: @YES,
            (id)kSecAttrLabel: @"si-44-seckey-aks:sepsyskey1",
        },
    }, (void *)&error));
    isnt(itemCreatedKey, nil, "failed to generate system key into keychain");

    id itemKey;
    OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)@{
        (id)kSecClass: (id)kSecClassKey,
        (id)kSecAttrLabel: @"si-44-seckey-aks:sepsyskey1",
        (id)kSecUseSystemKeychainAlways: @YES,
        (id)kSecReturnRef: @YES,
    }, (void *)&itemKey);
    is(status, errSecSuccess, "query system SEP key from system keychain");

    error = nil;
    signature = CFBridgingRelease(SecKeyCreateSignature((SecKeyRef)itemKey, algorithm, (CFDataRef)message, (void *)&error));
    isnt(signature, nil, "system session key restored from system keychain failed: %@", error);

    SecItemDelete((__bridge CFDictionaryRef)@{
        (id)kSecClass: (id)kSecClassKey,
        (id)kSecUseSystemKeychainAlways: @YES,
        (id)kSecAttrLabel: @"si-44-seckey-aks:sepsyskey1",
    });
#endif
#endif
}

int si_44_seckey_aks(int argc, char *const *argv) {
    @autoreleasepool {
        NSNumber *hasSEP = CFBridgingRelease(MGCopyAnswer(kMGQHasSEP, NULL));
        if (!hasSEP.boolValue) {
            // macOS without SEP cannot run attestations at all.
            plan_tests(1);
            ok(true);
            return 0;
        }

        NSNumber *hasPKA = CFBridgingRelease(MGCopyAnswer(kMGQHasPKA, NULL));
        plan_tests(hasPKA.boolValue ? 238 : 120);

        secAccessControlDescriptionTest();
        secKeySepTest(hasPKA.boolValue);

        attestationTest(kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly, YES);
        attestationTest(kSecAttrAccessibleUntilReboot, NO);

        sysKeyAttestationTest(kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly, YES, "SysUIK", kSecKeySystemKeyTypeUIKCommitted, kSecKeySystemKeyTypeUIKProposed, YES);
        sysKeyAttestationTest(kSecAttrAccessibleUntilReboot, NO, "SysUIK", kSecKeySystemKeyTypeUIKCommitted, kSecKeySystemKeyTypeUIKProposed, YES);

        // OIK is too weird to be usable directly, just skip is testing for now.
#if 0
        sysKeyAttestationTest(kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly, YES, "OIK", kSecKeySystemKeyTypeOIKCommitted, kSecKeySystemKeyTypeOIKProposed, NO);
        sysKeyAttestationTest(kSecAttrAccessibleUntilReboot, NO, "OIK", kSecKeySystemKeyTypeOIKCommitted, kSecKeySystemKeyTypeOIKProposed, NO);
#endif

        sysKeyAttestationTest(kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly, YES, "DAK", kSecKeySystemKeyTypeDAKCommitted, kSecKeySystemKeyTypeDAKProposed, YES);
        sysKeyAttestationTest(kSecAttrAccessibleUntilReboot, NO, "DAK", kSecKeySystemKeyTypeDAKCommitted, kSecKeySystemKeyTypeDAKProposed, YES);

        sysKeyAttestationTest(kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly, YES, "SDAK", kSecKeySystemKeyTypeSDAKCommitted, kSecKeySystemKeyTypeSDAKProposed, NO /* canAttest */);
        sysKeyAttestationTest(kSecAttrAccessibleUntilReboot, NO, "SDAK", kSecKeySystemKeyTypeSDAKCommitted, kSecKeySystemKeyTypeSDAKProposed, NO /* canAttest */);

        sysKeyAttestationTest(kSecAttrAccessibleUntilReboot, NO, "SDAK", kSecKeySystemKeyTypeSDAKCommitted, kSecKeySystemKeyTypeSDAKProposed, NO /* canAttest */);

        keyFromBlobTest();
        keychainTest();

        if (hasPKA.boolValue) {
            // Put SEP keys into test-keybag mode. Available only when running in direct-mode, not with remote SEPKey.
            [TKSEPKey setupKeybagForTesting:YES];
            rewrapTest();
            [TKSEPKey setupKeybagForTesting:NO];
        }

        // Test signing with haven keys.
        sysKeySignTest(kSecKeySystemKeyTypeHavenCommitted, kSecKeyAlgorithmECDSASignatureMessageX962SHA384, 384, "Haven-C");
        sysKeySignTest(kSecKeySystemKeyTypeHavenProposed, kSecKeyAlgorithmECDSASignatureMessageX962SHA384, 384, "Haven-P");

        // Test using system session keys.
        systemSessionKeysTest();

        return 0;
    }
}
